package com.ynet.middleground.contract.model;

import java.io.*;
import java.time.LocalDateTime;
import java.util.*;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.data.PictureRenderData;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ynet.core.common.BaseReqObj;
import com.ynet.core.common.PagerInfo;
import com.ynet.core.exception.BusinessException;
import com.ynet.file.service.FileService;
import com.ynet.file.starter.FileServiceConfiguration;
import com.ynet.middleground.contract.bean.*;
import com.ynet.middleground.contract.constant.OperationTypeEnum;
import com.ynet.middleground.contract.constant.SystemConsts;
import com.ynet.middleground.contract.constant.TemplateConsts;
import com.ynet.middleground.contract.dao.*;
import com.ynet.middleground.contract.dto.*;
import com.ynet.middleground.contract.entity.*;
import com.ynet.middleground.contract.service.impl.ContractManagementServiceImpl;
import com.ynet.middleground.contract.utils.BeanUtil;
import com.ynet.middleground.contract.utils.CommUtils;
import com.ynet.middleground.contract.utils.GeneralService;
import com.ynet.middleground.user.bean.QueryUserInfoReq;
import com.ynet.middleground.user.dto.UserInfoDto;

/**
 * 合约管理事务业务处理
 *
 * @author daigaole
 */
@Component
public class ContractManagementModel extends ServiceImpl<ContractMapper, Contract> {

    /**
     * 操作成功
     */
    public static final String SUCCESSFUL = "操作成功";

    /**
     * 根据对象的属性复制到另一对象上的同名属性值
     */
    @Autowired
    Mapper dozerBeanMapper;

    /**
     * 合约主表dao
     */
    @Autowired
    ContractMapper contractMapper;

    /**
     * 档案主表dao
     */
    @Autowired
    ArchiveMapper archiveMapper;

    /**
     * 模版dao
     */
    @Autowired
    TemplateMapper templateMapper;

    /**
     * 合约历史表dao
     */
    @Autowired
    ContractHistoryMapper contractHistoryMapper;

    /**
     * 档案历史表dao
     */
    @Autowired
    ArchiveHistoryMapper archiveHistoryMapper;

    /**
     * 通用公共方法服务类
     */
    @Autowired
    GeneralService generalService;

    /**
     * 模板工具类
     */
    @Autowired
    private TemplateModel templateModel;

    /**
     * 模板参数业务实现类
     */
    @Autowired
    private TemplateParamModel templateParamModel;

    /**
     * 模板操作日志记录
     */
    @Autowired
    private TemplateOperationLogModel templateOperationLogModel;

    /**
     * 档案管理事务业务处理
     */
    @Autowired
    private ArchiveManagementModel archiveManagementModel;

    /**
     * oss服务工具类
     */
    @Autowired
    private FileService fileService;

    /**
     * 合约类型业务处理类
     */
    @Autowired
    private ContractTypeModel contractTypeModel;

    /**
     * oss服务工具类
     */
    @Autowired
    FileServiceConfiguration fileServiceConfiguration;

    /**
     * 通用工具类
     */
    @Autowired
    CommUtils commUtils;

    @Autowired
    ContractNoMapper contractNoMapper;

    @Autowired
    UserCenterModel userCenterModel;

    /**
     * 新建合约，根据档案模版生产档案
     *
     * @param requestObject 新建合约请求对象
     * @return 新建合约响应传输对象
     */
    @Transactional(rollbackFor = Exception.class)
    public NewContractDto newContract(NewContractReq requestObject) {
        Integer operationUserId = requestObject.getOperationUserId();
        // 校验操作员id是否有效
        QueryUserInfoReq queryUserInfoReq = generalService.getQueryUserInfoReq(requestObject, operationUserId);
        if (!generalService.isUserIdCorrect(queryUserInfoReq)) {
            throw new BusinessException("用户id不存在", "ECCT0102");
        }

        // 若上送业态 id，则查询业态是否有效
        if (null != requestObject.getFormatId()) {
            userCenterModel.getFormatOrgInfo(requestObject.getFormatId(), requestObject);
        }

        // 合约编号
        String contractNo = null;
        // 如果上送了合约编号，则使用上送的，否则由中台自己生成
        if (org.apache.commons.lang3.StringUtils.isNotBlank(requestObject.getContractNo())) {
            // 校验合约编号规则
            checkContractNoRule(requestObject.getContractNo());
            // 校验是否重复
            checkContractNoRepeat(requestObject.getContractNo());
            contractNo = requestObject.getContractNo();
        }
        Integer templateId = requestObject.getTemplateId();
        Integer parentId = requestObject.getParentId();
        Map<String, Object> parameter = requestObject.getParameter();
        if (parameter == null) {
            parameter = new HashMap<>();
        }
        File file = null;
        // 生成合约文件上送的oss地址
        String ossUrl = null;
        Integer id = null;
        NewContractDto newContractDto = new NewContractDto();
        if (templateId == null) {// 如果模版id是空那么就是非制合约
            if (StringUtils.isEmpty(requestObject.getNonStandardContractOss())) {
                throw new BusinessException("非制式合约地址不能为空", "ECCT0715");
            }
            // 获取文件后缀
            String suffix = requestObject.getNonStandardContractOss()
                .substring(requestObject.getNonStandardContractOss().lastIndexOf(".") + 1);

            // 如果是 PDF 合约，无需上送合约编号 key、二维码 key 参数
            if ("PDF".equalsIgnoreCase(suffix)) {
                nonStandardNotEmptyCheckForPDF(requestObject);
            } else {
                nonStandardNotEmptyInspection(requestObject);
            }

            Contract contract = dozerBeanMapper.map(requestObject, Contract.class);

            // 合约状态为未签署状态
            contract.setStatus(SystemConsts.CONTRACT_STATUS_0);

            // 如果存在上级合约id 必须有效
            if (parentId != null && parentId > 0) {
                generalService.getParentContractById(parentId);
            }
            contract.setContractName(requestObject.getName());
            // 是否是非制式合约 是TINYINT(1)的同义词。zero值被视为假。非zero值视为真。
            contract.setIsNonStandard(true);
            contract.setCustomerManager(operationUserId);
            contract.setNonStandardContractOss(requestObject.getNonStandardContractOss());
            generalService.contractUpdateOperationIdAndDate(contract, operationUserId, false);

            contractMapper.insert(contract);
            // 当id为1000000的整数倍时，跳过该条记录，以避免生成合约名时id取值为000000
            if (contract.getId() % 1000000 == 0) {
                contractMapper.deleteById(contract.getId());
                contractMapper.insert(contract);
            }
            id = contract.getId();
            String contractTypeCode = requestObject.getContractTypeCode();
            contract.setContractTypeCode(contractTypeCode);
            // 产品类型都用上送的。
            contract.setProductType(requestObject.getProductType());

            if (org.apache.commons.lang3.StringUtils.isBlank(requestObject.getContractNo())) {
                contractNo = generalService.getContractNo(requestObject.getChannel(), requestObject.getCompanyCode(),
                    contractTypeCode, id, requestObject);
            }
            contract.setContractNo(contractNo);
            // 如果非制式文件是 PDF 格式，则无需渲染，
            if ("PDF".equalsIgnoreCase(suffix)) {
                contract.setOssUrl(null);
                contract.setPdfOssUrl(requestObject.getNonStandardContractOss());
            } else {
                String placeholderKey = requestObject.getPlaceholderKey();
                parameter.put(placeholderKey, contractNo);
                String codeQrKey = requestObject.getCodeQrKey();
                byte[] qrcode = generalService.getQRCodeImage(contractNo, TemplateConsts.TPL_PARAM_QR_CODE_WEIGHT,
                    TemplateConsts.TPL_PARAM_QR_CODE_HEIGHT);
                parameter.put(codeQrKey, new PictureRenderData(TemplateConsts.TPL_PARAM_QR_CODE_WEIGHT,
                    TemplateConsts.TPL_PARAM_QR_CODE_HEIGHT, ".png", qrcode));
                file = getNonContract(requestObject.getNonStandardContractOss(), parameter);

                ossUrl = getOssUrl(file, contractNo, null);
                contract.setOssUrl(ossUrl);
                String generatePDFUrl = generalService.generatePDF(ossUrl, requestObject);
                contract.setPdfOssUrl(generatePDFUrl);
                if (requestObject.getParameter() == null || requestObject.getParameter().isEmpty()) {
                    contract.setParameter(null);
                }
            }
            contractMapper.updateById(contract);
            newContractDto.setContractNo(contract.getContractNo());
            newContractDto.setPdfOssUrl(contract.getPdfOssUrl());
            newContractDto.setId(contract.getId());
        } else {
            /*
             * 模板 id 对应模板必须存在且有效
             * */
            Template template = generalService.getTemplate(templateId, requestObject.getChannel());

            // 模版类型 档案 - 还是合约
            String type = template.getType();
            // 模板类型 0：合同模板、1：平台协议模板、2：档案模板
            if (TemplateConsts.TPL_TYPE_CONTRACT.equals(type)) {// 1-合同模板 新建制式合约
                // 新建制式合约非空检验
                standardNotEmptyInspection(requestObject);

                Contract contract = dozerBeanMapper.map(requestObject, Contract.class);

                // 合约状态为未签署状态
                contract.setStatus(SystemConsts.CONTRACT_STATUS_0);
                // 如果存在上级合约id 必须有效
                if (parentId != null && parentId > 0) {
                    generalService.getParentContractById(parentId);
                }
                contract.setContractName(requestObject.getName());
                // 是否是非制式合约 是TINYINT(1)的同义词。zero值被视为假。非zero值视为真。
                contract.setIsNonStandard(false);
                // 生成合约id
                generalService.contractUpdateOperationIdAndDate(contract, operationUserId, false);
                contract.setCustomerManager(operationUserId);

                contractMapper.insert(contract);
                // 当id为1000000的整数倍时，跳过该条记录，以避免生成合约名时id取值为000000
                if (contract.getId() % 1000000 == 0) {
                    contractMapper.deleteById(contract.getId());
                    contractMapper.insert(contract);
                }
                id = contract.getId();
                String contractTypeCode = getContractTypeCode(template);
                contract.setContractTypeCode(contractTypeCode);

                if (org.apache.commons.lang3.StringUtils.isBlank(requestObject.getContractNo())) {
                    contractNo = generalService.getContractNo(requestObject.getChannel(),
                        requestObject.getCompanyCode(), contractTypeCode, id, requestObject);
                }

                // 合约编号参数key
                String contractNoName = templateParamModel.getContractNoName(templateId);
                // 合约二维码的参数key
                String contractNoQrCodeName = templateParamModel.getContractNoQrCodeName(templateId);
                if (StringUtils.isNotEmpty(contractNoName)) {
                    parameter.put(contractNoName, contractNo);
                }
                // 二维码字节流
                byte[] qrcode = generalService.getQRCodeImage(contractNo, TemplateConsts.TPL_PARAM_QR_CODE_WEIGHT,
                    TemplateConsts.TPL_PARAM_QR_CODE_HEIGHT);
                if (StringUtils.isNotEmpty(contractNoQrCodeName)) {
                    parameter.put(contractNoQrCodeName, qrcode);
                }
                // 生成合约文件
                file = getContract(templateId, parameter, requestObject.getTableMergeCells());
                // 记录模版使用日志
                templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.INVOKE, requestObject);
                contract.setContractNo(contractNo);
                // 产品类型都用上送的。
                contract.setProductType(requestObject.getProductType());

                ossUrl = getOssUrl(file, contractNo, null);
                contract.setOssUrl(ossUrl);
                String generatePDFUrl = generalService.generatePDF(ossUrl, requestObject);
                contract.setPdfOssUrl(generatePDFUrl);
                if (requestObject.getParameter() == null || requestObject.getParameter().isEmpty()) {
                    contract.setParameter(null);
                }
                contractMapper.updateById(contract);
                newContractDto.setContractNo(contract.getContractNo());
                newContractDto.setPdfOssUrl(generatePDFUrl);
            } else if (TemplateConsts.TPL_TYPE_ARCHIVE.equals(type)) {// 2-档案模板
                archiveNotEmptyInspection(requestObject);
                // 渠道端业务id（必填），参数（key-value）（非必填），档案名称（必填）
                TemplateCommReq req = new TemplateCommReq();
                dozerBeanMapper.map(requestObject, req);
                // 生成文档文件
                file = getContract(templateId, parameter, requestObject.getTableMergeCells());

                Archive archive = dozerBeanMapper.map(requestObject, Archive.class);
                if (template.getContractTypeId() != null) {
                    String contractTypeCode = getContractTypeCode(template);
                    archive.setContractTypeCode(contractTypeCode);
                }

                // add by liwq on 2021-09-30
                // 添加档案涉及人名称，涉及企业名称
                if (requestObject.getArchiveNaturalPersonId() != null
                    && requestObject.getArchiveEnterpriseId() != null) {
                    throw new BusinessException("档案涉及自然人ID和档案涉及企业ID不能同时上送", "ECCT0103");
                }
                UserInfoDto userInfo = null;
                if (requestObject.getArchiveNaturalPersonId() != null) {
                    userInfo = userCenterModel.getUserInfo(requestObject.getArchiveNaturalPersonId(),
                        requestObject.getChannel(), requestObject.getOperationUserId(),
                        requestObject.getEnterpriseId());
                    if (userInfo != null) {
                        archive.setArchiveNaturalPersonName(userInfo.getUserBaseInformationDto().getUserName());
                    }
                }
                if (requestObject.getArchiveEnterpriseId() != null) {
                    userInfo =
                        userCenterModel.getUserInfo(requestObject.getArchiveEnterpriseId(), requestObject.getChannel(),
                            requestObject.getOperationUserId(), requestObject.getEnterpriseId());
                    if (userInfo != null) {
                        archive.setArchiveEnterpriseName(userInfo.getUserBaseInformationDto().getUserName());
                    }
                }
                // add by liwq on 2021-09-30

                archive.setProductType(requestObject.getProductType());
                generalService.archiveUpdateOperationIdAndDate(archive, operationUserId, false);
                archive.setName(requestObject.getName());
                archiveMapper.insert(archive);
                // 记录模版使用日志
                templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.INVOKE, requestObject);
                archiveManagementModel.insertArchiveOperationLog(archive.getId(),
                    SystemConsts.ARCHIVE_LOG_OPERATION_TYPE, requestObject);
                id = archive.getId();
                ossUrl = getOssUrl(file, "systemContract" + id + archive.getChannel() + archive.getBusId(), null);
                archive.setUrl(ossUrl);
                archive.setGmtSubmission(archive.getGmtCreate());
                archiveMapper.updateById(archive);
            } else {
                throw new BusinessException("平台协议模版，不能调用此接口", "ECCT0736");
            }
            newContractDto.setId(id);
            newContractDto.setOssUrl(ossUrl);
        }
        return newContractDto;
    }

    /**
     * 根据档案模版生产档案，非空字段校验
     *
     * @param requestObject 新建合约请求对象
     */
    public void archiveNotEmptyInspection(NewContractReq requestObject) {
        // if (StringUtils.isEmpty(requestObject.getProductType())) {
        // throw new BusinessException("产品类型不能非空", "ECCT0718");
        // }
    }

    /**
     * 非制式非空检验<br>
     * 适合无需中台进行模板渲染的情况
     *
     * @param requestObject 新建合约请求对象
     * @author liulx
     * @date 2020-10-26 10:26:56
     */
    public void nonStandardNotEmptyCheckForPDF(NewContractReq requestObject) {
        // if (StringUtils.isEmpty(requestObject.getSignatoryId())) {
        // throw new BusinessException("签署方Id不能为空", "ECCT0733");
        // }
        if (StringUtils.isEmpty(requestObject.getSignatoryName())) {
            throw new BusinessException("签署方名称不能为空", "ECCT0733");
        }

        if (StringUtils.isEmpty(requestObject.getSignedWay())) {
            throw new BusinessException("签署方式不能非空", "ECCT0714");
        }

        if (StringUtils.isEmpty(requestObject.getContractTypeCode())) {
            throw new BusinessException("合约类型Code不能为空", "ECCT0719");
        }
    }

    /**
     * 非制式非空检验
     *
     * @param requestObject 新建合约请求对象
     */
    public void nonStandardNotEmptyInspection(NewContractReq requestObject) {
        if (StringUtils.isEmpty(requestObject.getNonStandardContractOss())) {
            throw new BusinessException("非制式合约地址不能为空", "ECCT0715");
        }

        // if (StringUtils.isEmpty(requestObject.getSignatoryId())) {
        // throw new BusinessException("签署方Id不能为空", "ECCT0734");
        // }

        if (StringUtils.isEmpty(requestObject.getSignatoryName())) {
            throw new BusinessException("签署方名称不能为空", "ECCT0733");
        }

        if (StringUtils.isEmpty(requestObject.getSignedWay())) {
            throw new BusinessException("签署方式不能非空", "ECCT0714");
        }
        // 非制式二维码key（非必填，如果是非制为必填），非制式合约编号占位符key（非必填，如果是非制为必填）
        if (StringUtils.isEmpty(requestObject.getCodeQrKey())) {
            throw new BusinessException("非制式二维码key不能非空", "ECCT0716");
        }
        if (StringUtils.isEmpty(requestObject.getPlaceholderKey())) {
            throw new BusinessException("非制式合约编号占位符key不能非空", "ECCT0717");
        }

        // if (StringUtils.isEmpty(requestObject.getProductType())) {
        // throw new BusinessException("产品类型不能非空", "ECCT0718");
        // }
        if (StringUtils.isEmpty(requestObject.getContractTypeCode())) {
            throw new BusinessException("合约类型Code不能为空", "ECCT0719");
        }

    }

    /**
     * 制式非空检验
     *
     * @param requestObject 新建合约请求对象
     */
    public void standardNotEmptyInspection(NewContractReq requestObject) {
        if (StringUtils.isNotEmpty(requestObject.getNonStandardContractOss())
            || StringUtils.isNotEmpty(requestObject.getCodeQrKey())
            || StringUtils.isNotEmpty(requestObject.getPlaceholderKey())
            || StringUtils.isNotEmpty(requestObject.getContractTypeCode())) {
            throw new BusinessException("创建制式合约不能上传非制式合约参数", "ECCT0729");
        }

        // if (StringUtils.isEmpty(requestObject.getProductType())) {
        // throw new BusinessException("产品类型不能非空", "ECCT0718");
        // }

        if (StringUtils.isEmpty(requestObject.getSignedWay())) {
            throw new BusinessException("签署方式不能非空", "ECCT0714");
        }

        if (StringUtils.isEmpty(requestObject.getSignatoryName())) {
            throw new BusinessException("签署方名称不能为空", "ECCT0733");
        }
        // if (StringUtils.isEmpty(requestObject.getSignatoryId())) {
        // throw new BusinessException("签署方Id不能为空", "ECCT0734");
        // }
    }

    /**
     * 根据模版获取合约类型code
     *
     * @param template 模版信息
     * @return 合约类型code
     */
    public String getContractTypeCode(Template template) {
        ContractType contractType =
            contractTypeModel.validContractType(template.getContractTypeId(), template.getChannel());
        return contractType.getCode();
    }

    /**
     * 生成制式合约
     *
     * @param templateId 模版id
     * @param reqParams 模版参数
     * @param tableMergeCells 表格单元格合并数据信息
     * @return 文件对象
     */
    public File getContract(Integer templateId, Map<String, Object> reqParams,
        List<MergeCellTableInfo> tableMergeCells) {
        File file = templateModel.generateContract(templateId, reqParams, tableMergeCells);
        return file;
    }

    /**
     * 生成非制式合约
     *
     * @param nonStandardContractOss 非制式合约短路径地址
     * @param reqParams 参数
     * @return
     */
    public File getNonContract(String nonStandardContractOss, Map<String, Object> reqParams) {
        String ossFullUrl = commUtils.getOssFullUrl(nonStandardContractOss);
        return templateModel.generateWord(ossFullUrl, reqParams);
    }

    /**
     * 上传文件到oss服务器，返回url
     *
     * @param file 文件
     * @param contractNo 合约编号
     * @param suffix 文件后缀
     * @return 文件 url 短路径地址
     */
    public String getOssUrl(File file, String contractNo, String suffix) {
        String returnOss = null;
        if (suffix == null) {
            suffix = ".docx";
        }
        try {
            returnOss = fileService.uploadFile("oss", new FileInputStream(file),
                "contract" + Calendar.getInstance().getTimeInMillis() + suffix, commUtils.getOssBucketName());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }

        // 以下代码适合 ECS 测试环境及生产环境使用 | start
        // String endpoint = fileServiceConfiguration.getConfigs().get(0).getOssConfig().getEndpoint();
        // returnOss = endpoint.substring(0, endpoint.lastIndexOf("/")) + "/" + returnOss;
        // end

        // NOTE: 以下处理方式仅适合 205 环境使用
        /*String endpoint = fileServiceConfiguration.getConfigs().get(0).getOssConfig().getEndpoint();
        String str[] = returnOss.split("/");
        returnOss = endpoint.substring(0, endpoint.lastIndexOf("/") + 1) + "" + str[0] + "."
            + endpoint.substring(endpoint.lastIndexOf("/") + 1, endpoint.length()) + "/" + str[1];*/

        return returnOss;
    }

    /**
     * 合约查询
     *
     * @param requestObject 合约查询请求
     * @return 合约列表
     */
    public List<ContractDto> queryContract(QueryContractReq requestObject) {
        String channel = requestObject.getChannel();
        // 校验操作员id是否有效
        QueryUserInfoReq queryUserInfoReq =
            generalService.getQueryUserInfoReq(requestObject, requestObject.getOperationUserId());
        if (!generalService.isUserIdCorrect(queryUserInfoReq)) {
            throw new BusinessException("用户id不存在", "ECCT0102");
        }

        PagerInfo pagerInfo = requestObject.getPagerInfo();
        Page<Contract> contractPage = new Page<>(pagerInfo.getPageNum(), pagerInfo.getPageSize());
        // 合约id
        Integer contractId = requestObject.getContractId();
        String contractNo = requestObject.getContractNo();

        // 参数
        QueryWrapper<Contract> queryWrapper = fillQueryWapper(requestObject);

        // 查询合约
        IPage<Contract> contractIPage = this.contractMapper.selectPage(contractPage, queryWrapper);

        List<ContractDto> contractDtos = new ArrayList<>();

        List<Contract> records = contractIPage.getRecords();
        if (contractIPage.getRecords() != null) {
            records.forEach(contract -> {
                ContractDto contractDto = dozerBeanMapper.map(contract, ContractDto.class);

                contractDtos.add(contractDto);
            });
        }

        pagerInfo.setTotal(contractIPage.getTotal());

        return contractDtos;

    }

    /**
     * 拼装查询条件
     *
     * @param requestObject 合约查询请求
     * @return 合约查询对象
     */
    private QueryWrapper<Contract> fillQueryWapper(QueryContractReq requestObject) {
        QueryWrapper<Contract> queryWrapper = new QueryWrapper<>();
        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 start
        // queryWrapper.eq("channel", requestObject.getChannel());
        // 跨渠道访问交由内管控制 不由渠道端自行控制 by yangbh in 20200708 end
        queryWrapper.eq("is_deleted", SystemConsts.IS_DELETED_0);

        if (requestObject.getFormatId() != null) {
            queryWrapper.eq("format_id", requestObject.getFormatId());
        }

        if (StringUtils.isNotEmpty(requestObject.getContractNo())) {
            queryWrapper.eq("contract_no", requestObject.getContractNo());
        }
        if (requestObject.getContractId() != null && requestObject.getContractId() != 0) {
            queryWrapper.eq("id", requestObject.getContractId());
        }
        // 上级合约id
        Integer parentId = requestObject.getParentId();
        if (parentId != null && parentId != 0) {
            queryWrapper.eq("parent_id", parentId);
        }

        // 合约模版id
        Integer templateId = requestObject.getTemplateId();
        if (templateId != null && templateId != 0) {
            queryWrapper.eq("template_id", templateId);
        }

        // 合约类型
        String contractType = requestObject.getContractType();
        if (StringUtils.isNotEmpty(contractType)) {
            queryWrapper.eq("contract_type_code", contractType);
        }

        // 客户经理
        Integer customerManager = requestObject.getCustomerManager();
        if (customerManager != null && customerManager != 0) {
            queryWrapper.eq("customer_manager", customerManager);
        }

        // 业务流水号
        String serialNumber = requestObject.getSerialNumber();
        if (StringUtils.isNotEmpty(serialNumber)) {
            queryWrapper.eq("serial_number", serialNumber);
        }

        // 业务 id
        // String busId = requestObject.getBusId();
        // if (StringUtils.isNotEmpty(busId)) {
        // queryWrapper.eq("bus_id", busId);
        // }

        // 审批流程编号
        String appProcessNo = requestObject.getAppProcessNo();
        if (StringUtils.isNotEmpty(appProcessNo)) {
            queryWrapper.eq("app_process_no", appProcessNo);
        }

        if (StringUtils.isNotEmpty(requestObject.getTsignatoryDateStart())
            && StringUtils.isNotEmpty(requestObject.getTsignatoryDateEnd())) {
            // 签署时间
            queryWrapper.between("tsignatory_date", requestObject.getTsignatoryDateStart() + " 00:00:00",
                requestObject.getTsignatoryDateEnd() + " 23:59:59");
        } else {
            if (StringUtils.isNotEmpty(requestObject.getTsignatoryDateStart())) {
                queryWrapper.ge("tsignatory_date", requestObject.getTsignatoryDateStart() + " 00:00:00");
            }
            if (StringUtils.isNotEmpty(requestObject.getTsignatoryDateEnd())) {
                queryWrapper.le("tsignatory_date", requestObject.getTsignatoryDateEnd() + " 23:59:59");
            }
        }

        // update by liwq on 2020-10-29 start
        // 增加按照业务主体ID，业务主体名称，签署方Id，签署方名称查询
        // 业务主体
        String subjectId = requestObject.getSubjectId();
        if (StringUtils.isNotEmpty(subjectId)) {
            queryWrapper.eq("subject_id", subjectId);
        }
        String subjectName = requestObject.getSubjectName();
        if (StringUtils.isNotEmpty(subjectName)) {
            queryWrapper.like("subject_name", subjectName);
        }
        // 签署方
        String signatoryId = requestObject.getSignatoryId();
        if (StringUtils.isNotEmpty(signatoryId)) {
            queryWrapper.apply(StringUtils.isNotEmpty(signatoryId), "FIND_IN_SET (" + signatoryId + ",signatory_id)");
        }
        String signatoryName = requestObject.getSignatoryName();
        if (StringUtils.isNotEmpty(signatoryName)) {
            queryWrapper.like("signatory_name", signatoryName);
        }
        // update by liwq on 2020-10-29 end

        // 产品类型
        String productType = requestObject.getProductType();
        if (StringUtils.isNotEmpty(productType)) {
            queryWrapper.like("product_type", productType);
        }

        // 合约名称
        String contractName = requestObject.getContractName();
        if (StringUtils.isNotEmpty(contractName)) {
            queryWrapper.like("contract_name", contractName);
        }

        // 业务 id
        // update by liwq on 2021-08-17 start
        // busId 改成支持模糊查询
        String busId = requestObject.getBusId();
        if (StringUtils.isNotEmpty(busId)) {
            queryWrapper.like("bus_id", busId);
        }
        // update by liwq on 2021-08-17 end

        // 签署方式
        String signedWay = requestObject.getSignedWay();
        if (StringUtils.isNotEmpty(signedWay)) {
            queryWrapper.eq("signed_way", signedWay);
        }

        // 合约状态：0-未签署（新建保存状态下）、1-签署中、2-已签署、3-已结束
        String status = requestObject.getStatus();
        if (StringUtils.isNotEmpty(status)) {
            queryWrapper.eq("status", status);
        }

        // update by liwq on 2021-08-17 start
        // 按照合约创建时间排序（和按照ID排序效果一样，而且速度还快，所以这里写ID）
        queryWrapper.orderByDesc("id");
        // update by liwq on 2021-08-17 end

        return queryWrapper;
    }

    /**
     * 获取合约档案列表
     *
     * @param contractId 合约id
     * @return 档案详情信息传输对象
     */
    private List<ArchiveDto> getArchiveDto(Integer contractId) {
        List<ArchiveDto> archiveDtos = new ArrayList<>();
        QueryWrapper<Archive> archiveWhere = new QueryWrapper<>();
        archiveWhere.eq("contract_id", contractId);
        List<Archive> archives = archiveMapper.selectList(archiveWhere);
        archives.forEach(archive -> {
            ArchiveDto archiveDto = dozerBeanMapper.map(archive, ArchiveDto.class);
            archiveDtos.add(archiveDto);
        });
        return archiveDtos;
    }

    /**
     * 分页处理合约查询数据
     *
     * @param contractPage 分页工具
     * @param queryWrapper 分页查询条件
     * @param contractDtos 合约详情信息传输对象
     * @param pagerInfo 分页信息对象
     */
    private void contractPage(Page<Contract> contractPage, QueryWrapper<Contract> queryWrapper,
        List<ContractDto> contractDtos, PagerInfo pagerInfo) {
        List<Contract> records = null;
        IPage<Contract> page = this.page(contractPage, queryWrapper);
        records = page.getRecords();
        if (records != null && records.size() == 0) {
            return;
        }
        records.forEach(contract -> {
            ContractDto contractDto = dozerBeanMapper.map(contract, ContractDto.class);
            String parameter = contract.getParameter();
            if (parameter != null) {
                ObjectMapper mapper = new ObjectMapper();
                Map<String, Object> m = null;
                try {
                    m = mapper.readValue(parameter, Map.class);
                } catch (IOException e) {
                    throw new BusinessException("json转换map异常", "ECCT0704");
                }
                contractDto.setParameter(m);
            }
            contractDtos.add(contractDto);
        });
        pagerInfo.setTotal(page.getTotal());
    }

    /**
     * 修改合约
     *
     * @param requestObject 修改合约请求
     * @return 提示信息
     */
    @Transactional(rollbackFor = Exception.class)
    public String modifyContract(ModifyContractReq requestObject) {
        // 模板 id不能为空
        Integer contractId = requestObject.getContractId();
        // 操作人id
        Integer operationUserId = requestObject.getOperationUserId();
        // 渠道
        String channel = requestObject.getChannel();

        // 校验操作员id是否有效
        QueryUserInfoReq queryUserInfoReq =
            generalService.getQueryUserInfoReq(requestObject, requestObject.getOperationUserId());
        if (!generalService.isUserIdCorrect(queryUserInfoReq)) {
            throw new BusinessException("用户id不存在", "ECCT0102");
        }

        // Contract contract = contractMapper.selectById(contractId);
        Contract contract = generalService.verifyContractIdEffective(contractId);
        // 合约id必须存在且有效
        if (contract == null) {
            throw new BusinessException("合约id无效", "ECCT0703");
        }

        // 可修改合约的规则：合约状态为“未签署”，合同不能有在途流程。
        if (!contract.getStatus().equals(SystemConsts.CONTRACT_STATUS_0)) {
            throw new BusinessException("合约状态非未签署", "ECCT0707");
        }
        // 调用上送渠道必须和合约id的渠道一致
        if (!contract.getChannel().equals(channel)) {
            throw new BusinessException("上送渠道和当前渠道不一致", "ECCT0705");
        }

        // 调用上送操作员id必须和合约id的操作员id一致
        if (!contract.getCreateBy().equals(operationUserId)) {
            throw new BusinessException("修改人和创建人不一致", "ECCT0706");
        }

        // 合约已关联业态，修改时却未上送业态，则不允许修改
        if (contract.getFormatId() != null && requestObject.getFormatId() == null) {
            throw new BusinessException("该合约已关联业态信息，请上送所属业态id！", "ECCT0009");
        }

        // 修改时上送的业态与合约关联业态的不一致，则不允许修改
        if (null != requestObject.getFormatId() && !requestObject.getFormatId().equals(contract.getFormatId())) {
            throw new BusinessException("该合约关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }

        // 合约是否有在途流程
        generalService.checkOnTheProcess(contract.getAppProcessNo(), requestObject);

        // 记录修改历史记录
        updateContractHistory(contract);

        Map<String, Object> parameter = requestObject.getParameter();

        File file = null;
        String contractNo = contract.getContractNo();
        String ossUrl = null;
        if (contract.getTemplateId() == null) {// 是非制式合约

            if (parameter == null) {
                parameter = new HashMap<>();
            }

            if (StringUtils.isNotEmpty(requestObject.getNonStandardContractOss())) {
                // 获取文件后缀
                String suffix = requestObject.getNonStandardContractOss()
                    .substring(requestObject.getNonStandardContractOss().lastIndexOf(".") + 1);

                // 如果是 PDF 合约，无需上送合约编号 key、二维码 key 参数
                if ("PDF".equalsIgnoreCase(suffix)) {
                    contract.setPdfOssUrl(requestObject.getNonStandardContractOss());
                    contract.setNonStandardContractOss(requestObject.getNonStandardContractOss());
                } else {
                    // 非制式二维码key（非必填，如果是非制为必填），非制式合约编号占位符key（非必填，如果是非制为必填）
                    if (StringUtils.isEmpty(requestObject.getCodeQrKey())) {
                        throw new BusinessException("非制式二维码key不能非空", "ECCT0716");
                    }
                    if (StringUtils.isEmpty(requestObject.getPlaceholderKey())) {
                        throw new BusinessException("非制式合约编号占位符key不能非空", "ECCT0717");
                    }

                    String placeholderKey = requestObject.getPlaceholderKey();
                    parameter.put(placeholderKey, contractNo);
                    String codeQrKey = requestObject.getCodeQrKey();
                    byte[] qrcode = generalService.getQRCodeImage(contractNo, 200, 200);
                    parameter.put(codeQrKey, new PictureRenderData(200, 200, ".png", qrcode));
                    file = getNonContract(requestObject.getNonStandardContractOss(), parameter);

                    ossUrl = getOssUrl(file, contractNo, null);
                    String generatePDFUrl = generalService.generatePDF(ossUrl, requestObject);
                    contract.setPdfOssUrl(generatePDFUrl);
                    contract.setNonStandardContractOss(requestObject.getNonStandardContractOss());
                    contract.setCodeQrKey(requestObject.getCodeQrKey());
                    contract.setPlaceholderKey(requestObject.getPlaceholderKey());
                    contract.setOssUrl(ossUrl);
                }
            }

        } else {// 制式合约

            if (StringUtils.isNotEmpty(requestObject.getNonStandardContractOss())
                || StringUtils.isNotEmpty(requestObject.getPlaceholderKey())
                || StringUtils.isNotEmpty(requestObject.getCodeQrKey())) {
                throw new BusinessException("当前为制式合约，不能修改非制式信息", "ECCT0726");
            }

            if (parameter != null) {
                parameter = requestObject.getParameter();
                contract.setParameter(JSON.toJSONString(parameter));
                Integer templateId = contract.getTemplateId();
                // 合约编号参数key
                String contractNoName = templateParamModel.getContractNoName(templateId);
                // 合约二维码的参数key
                String contractNoQrCodeName = templateParamModel.getContractNoQrCodeName(templateId);

                parameter.put(contractNoName, contractNo);
                // 二维码字节流
                byte[] qrcode = generalService.getQRCodeImage(contractNo, TemplateConsts.TPL_PARAM_QR_CODE_WEIGHT,
                    TemplateConsts.TPL_PARAM_QR_CODE_HEIGHT);
                parameter.put(contractNoQrCodeName, qrcode);
                // 生成合约文件
                file = getContract(templateId, parameter, requestObject.getTableMergeCells());
                // 记录模版使用日志
                Template template = generalService.getTemplate(templateId, requestObject.getChannel());
                templateOperationLogModel.insertOperationLog(template, OperationTypeEnum.INVOKE, requestObject);
                ossUrl = getOssUrl(file, contractNo, null);
                contract.setOssUrl(ossUrl);
                String generatePDFUrl = generalService.generatePDF(ossUrl, requestObject);
                contract.setPdfOssUrl(generatePDFUrl);
            }
        }

        if (StringUtils.isNotEmpty(requestObject.getContractName())) {
            contract.setContractName(requestObject.getContractName());
        }

        if (StringUtils.isNotEmpty(requestObject.getSignatoryName())) {
            contract.setSignatoryName(requestObject.getSignatoryName());
        }

        // add by liwq on 2021-10-29 start
        // 修改业务主体ID，业务主体名称，签署方id，签署方名称
        if (StringUtils.isNotEmpty(requestObject.getSignatoryId())) {
            contract.setSignatoryId(requestObject.getSignatoryId());
        }
        // if (StringUtils.isNotEmpty(requestObject.getSubjectId())) {
        // contract.setSubjectId(requestObject.getSubjectId());
        // }
        // if (StringUtils.isNotEmpty(requestObject.getSubjectName())) {
        // contract.setSubjectName(requestObject.getSubjectName());
        // }
        // add by liwq on 2021-10-29 end

        if (StringUtils.isNotEmpty(requestObject.getDescription())) {
            contract.setDescription(requestObject.getDescription());
        }

        contractUpdateOperationIdAndDate(contract, operationUserId, true);
        contractMapper.updateById(contract);
        return SUCCESSFUL;

    }

    /**
     * 结束合约
     *
     * @param requestObj 结束合约请求对象
     * @return 提示信息
     */
    @Transactional(rollbackFor = Exception.class)
    public String overContract(OverContractReq requestObj) {
        Integer contractId = requestObj.getContractId();
        Integer operationUserId = requestObj.getOperationUserId();
        String channel = requestObj.getChannel();
        QueryWrapper<Contract> queryWrapper = new QueryWrapper<>();
        // 校验操作员id是否有效
        QueryUserInfoReq queryUserInfoReq =
            generalService.getQueryUserInfoReq(requestObj, requestObj.getOperationUserId());
        if (!generalService.isUserIdCorrect(queryUserInfoReq)) {
            throw new BusinessException("用户id不存在", "ECCT0102");
        }

        Contract contract = contractMapper.selectById(contractId);

        // 1、合约编号（合约编号）必须存在且有效
        if (contract == null) {
            throw new BusinessException("合约id无效", "ECCT0704");
        }

        if (!Integer.valueOf(SystemConsts.IS_DELETED_0).equals(contract.getIsDeleted())) {
            throw new BusinessException("合约已经删除", "ECCT0711");
        }

        if (!channel.equals(contract.getChannel())) {
            throw new BusinessException("上送渠道和当前渠道不一致", "ECCT0705");
        }

        // 合约已关联业态，结束时却未上送业态，则不允许结束
        if (contract.getFormatId() != null && requestObj.getFormatId() == null) {
            throw new BusinessException("该合约已关联业态信息，请上送所属业态id！", "ECCT0009");
        }

        // 结束时上送的业态与合约关联业态的不一致，则不允许结束
        if (null != requestObj.getFormatId() && !requestObj.getFormatId().equals(contract.getFormatId())) {
            throw new BusinessException("该合约关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }

        // 2、可结束合约的规则：合约状态为非“已结束”，合同不能有在途流程。
        if (SystemConsts.CONTRACT_STATUS_3.equals(contract.getStatus())) {
            throw new BusinessException("合约状态已是结束状态", "ECCT0709");
        }
        generalService.checkOnTheProcess(contract.getAppProcessNo(), requestObj);
        // 4、如果合约是父级，当前合约编号的下级合约状态全部更新为“结束状态”。
        overContractStatus(contract, operationUserId);
        return SUCCESSFUL;
    }

    public void overContractStatus(Contract contract, Integer operationUserId) {
        QueryWrapper<Contract> queryWrapper = new QueryWrapper<>();
        queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("parent_id", contract.getId());
        List<Contract> contractList = contractMapper.selectList(queryWrapper);
        if (contractList != null && contractList.size() != 0) {
            contractList.forEach(var -> {
                var.setStatus(SystemConsts.CONTRACT_STATUS_3);
                contractUpdateOperationIdAndDate(var, operationUserId, true);
                contractMapper.updateById(var);
            });
        }
        contractUpdateOperationIdAndDate(contract, operationUserId, true);
        contract.setStatus(SystemConsts.CONTRACT_STATUS_3);
        contractMapper.updateById(contract);
    }

    /**
     * 合约处理更新或新建操作
     *
     * @param contract 合约实体类
     * @param operationUserId 操作人id
     * @param isUpdate 是否是更新操作
     */
    private void contractUpdateOperationIdAndDate(Contract contract, Integer operationUserId, Boolean isUpdate) {
        if (isUpdate) {
            // 修改人
            contract.setModifiedBy(operationUserId);
            // 修改时间
            contract.setGmtModified(LocalDateTime.now());
        } else {
            // 创建人
            contract.setCreateBy(operationUserId);
            // 创建时间
            contract.setGmtCreate(LocalDateTime.now());
            // 修改人
            contract.setModifiedBy(operationUserId);
            // 修改时间
            contract.setGmtModified(LocalDateTime.now());
        }

    }

    /**
     * 档案处理更新或新建操作
     *
     * @param archive 档案实体类
     * @param operationUserId 操作人id
     * @param isUpdate 是否是更新操作
     */
    private void archiveUpdateOperationIdAndDate(Archive archive, Integer operationUserId, Boolean isUpdate) {
        if (isUpdate) {
            // 修改人
            archive.setModifiedBy(operationUserId);
            // 修改时间
            archive.setGmtModified(LocalDateTime.now());
        } else {
            // 创建人
            archive.setCreateBy(operationUserId);
            // 创建时间
            archive.setGmtCreate(LocalDateTime.now());
            // 修改人
            archive.setModifiedBy(operationUserId);
            // 修改时间
            archive.setGmtModified(LocalDateTime.now());
        }

    }

    /**
     * 更新记录档案信息到档案历史表
     *
     * @param archive 档案历史记录表
     */
    private void updateArchiveHistory(Archive archive) {
        ArchiveHistory archiveHistory = dozerBeanMapper.map(archive, ArchiveHistory.class);
        archiveHistory.setArchiveId(archive.getId());
        archiveHistoryMapper.insert(archiveHistory);
    }

    /**
     * 更新记录合约信息到合约历史表
     *
     * @param contract 合约历史表
     */
    private void updateContractHistory(Contract contract) {
        ContractHistory contractHistory = dozerBeanMapper.map(contract, ContractHistory.class);
        contractHistory.setContractId(contract.getId());
        contractHistoryMapper.insert(contractHistory);
    }

    /**
     * 校验合约编号（合约编号）必须存在且有效
     *
     * @param contractNo 合约编号
     * @return 合约对象
     */
    private Contract isContractNoEffective(String contractNo) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("contract_no", contractNo);
        Contract contract = contractMapper.selectOne(queryWrapper);
        // 1、合约编号（合约编号）必须存在且有效
        if (contract == null) {
            throw new BusinessException("合约编号无效", "ECCT0704");
        }
        return contract;
    }

    /**
     * 校验上送渠道和当前渠道是否一致
     *
     * @param contract 合约信息
     * @param channel 渠道
     */
    private void isChannelConsistent(Contract contract, String channel) {
        // 调用上送渠道必须和合约id的渠道一致
        if (!contract.getChannel().equals(channel)) {
            throw new BusinessException("上送渠道和当前渠道不一致", "ECCT0705");
        }
    }

    /**
     * 校验修改人和创建人是否一致
     *
     * @param contract 合约信息
     * @param operationUserId 操作人id
     */
    private void isOperationUserIdConsistent(Contract contract, Integer operationUserId) {
        // 调用上送操作员id必须和合约id的操作员id一致
        if (!contract.getCreateBy().equals(operationUserId)) {
            throw new BusinessException("修改人和创建人不一致", "ECCT0706");
        }
    }

    /**
     * 保存档案信息到合约
     *
     * @param contract 合约信息
     * @param contractArchiveList 档案信息
     * @param operationUserId 操作人id
     */
    private void saveContractArchiveList(Contract contract, List<ContractArchive> contractArchiveList,
        Integer operationUserId) {
        // 保存档案列表
        if (contractArchiveList != null && contractArchiveList.size() != 0) {
            contractArchiveList.forEach(contractArchive -> {
                Archive archive = dozerBeanMapper.map(contractArchive, Archive.class);
                // 合同id
                // archive.setContractId(contract.getId());
                archiveUpdateOperationIdAndDate(archive, operationUserId, false);
                archiveMapper.insert(archive);
            });
        }
    }

    /**
     * 变更合约
     *
     * @param requestObj 变更合约请求
     * @return 新建合约响应传输对象
     */
    @Transactional(rollbackFor = Exception.class)
    public NewContractDto changeContract(ChangeContractReq requestObj) {
        // 合约编号不能为空
        Integer parentId = requestObj.getParentId();
        String channel = requestObj.getChannel();
        Integer operationUserId = requestObj.getOperationUserId();
        Map<String, Object> parameter = new HashMap<>();
        File file = null;

        // 原合约id存在且有效,原合约状态为“已签署”
        Contract parentContract = generalService.checkContractById(parentId);

        generalService.checkOnTheProcess(parentContract.getAppProcessNo(), requestObj);
        // 校验是否为创建者。
        isOperationUserIdConsistent(parentContract, operationUserId);
        /// 校验渠道是否一致
        isChannelConsistent(parentContract, channel);

        // 原合约已关联业态，变更时时却未上送业态，则不允许变更
        if (parentContract.getFormatId() != null && requestObj.getFormatId() == null) {
            throw new BusinessException("该合约已关联业态信息，请上送所属业态id！", "ECCT0009");
        }

        // 变更时上送的业态与合约关联业态的不一致，则不允许变更
        if (null != requestObj.getFormatId() && !requestObj.getFormatId().equals(parentContract.getFormatId())) {
            throw new BusinessException("该合约关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }

        // 生成合约编号
        String contractNo = null;
        // 如果上送了合约编号，则使用上送的，否则由中台自己生成
        if (org.apache.commons.lang3.StringUtils.isNotBlank(requestObj.getContractNo())) {
            // 校验合约编号规则
            checkContractNoRule(requestObj.getContractNo());
            checkContractNoRepeat(requestObj.getContractNo());
            contractNo = requestObj.getContractNo();
        }

        Contract contract = dozerBeanMapper.map(requestObj, Contract.class);
        // 合约状态为未签署状态
        contract.setStatus(SystemConsts.CONTRACT_STATUS_0);
        contractUpdateOperationIdAndDate(contract, operationUserId, false);
        // 当tinyint为0时代表false，其它数值为true。
        contract.setIsChanges(true);
        // 是否是非制式合约 是TINYINT(1)的同义词。zero值被视为假。非zero值视为真。
        contract.setIsNonStandard(true);
        contract.setCustomerManager(operationUserId);
        contractMapper.insert(contract);
        // 生成新合同id
        Integer contractId = contract.getId();

        if (org.apache.commons.lang3.StringUtils.isBlank(requestObj.getContractNo())) {
            contractNo = generalService.getContractNo(requestObj.getChannel(), requestObj.getCompanyCode(),
                requestObj.getContractTypeCode(), contractId, requestObj);
        }
        contract.setContractNo(contractNo);

        // 获取文件后缀
        String suffix = requestObj.getNonStandardContractOss()
            .substring(requestObj.getNonStandardContractOss().lastIndexOf(".") + 1);

        // 如果该文件是 PDF 格式，无需上送合约编号 key、二维码 key 参数，且无需渲染
        if ("PDF".equalsIgnoreCase(suffix)) {
            contract.setOssUrl(requestObj.getNonStandardContractOss());
            contract.setPdfOssUrl(requestObj.getNonStandardContractOss());
            contractMapper.updateById(contract);
            NewContractDto newContractDto = new NewContractDto();
            newContractDto.setContractNo(contract.getContractNo());
            newContractDto.setId(contract.getId());
            newContractDto.setPdfOssUrl(requestObj.getNonStandardContractOss());
            return newContractDto;
        } else {
            String placeholderKey = requestObj.getPlaceholderKey();
            parameter.put(placeholderKey, contractNo);
            String codeQrKey = requestObj.getCodeQrKey();
            byte[] qrcode = generalService.getQRCodeImage(contractNo, 200, 200);
            parameter.put(codeQrKey, new PictureRenderData(200, 200, ".png", qrcode));
            // 生成oss文件
            file = getNonContract(requestObj.getNonStandardContractOss(), parameter);
            String ossUrl = getOssUrl(file, contractNo, null);
            contract.setOssUrl(ossUrl);
            String generatePDFUrl = generalService.generatePDF(ossUrl, requestObj);
            contract.setPdfOssUrl(generatePDFUrl);
            contractMapper.updateById(contract);
            NewContractDto newContractDto = new NewContractDto();
            newContractDto.setContractNo(contract.getContractNo());
            newContractDto.setId(contract.getId());
            newContractDto.setOssUrl(ossUrl);
            newContractDto.setPdfOssUrl(generatePDFUrl);
            return newContractDto;
        }
    }

    /**
     * 删除合约
     *
     * @param requestObj 删除合约请求对象
     * @return 提示信息
     */
    @Transactional(rollbackFor = Exception.class)
    public String deleteContract(DeleteContractReq requestObj) {
        Integer operationUserId = requestObj.getOperationUserId();
        String channel = requestObj.getChannel();

        // 校验操作员id是否有效
        QueryUserInfoReq queryUserInfoReq =
            generalService.getQueryUserInfoReq(requestObj, requestObj.getOperationUserId());
        if (!generalService.isUserIdCorrect(queryUserInfoReq)) {
            throw new BusinessException("用户id不存在", "ECCT0102");
        }

        // 合约id不能为空
        Integer contractId = requestObj.getContractId();

        Contract contract = generalService.verifyContractIdEffective(contractId);

        // 合约已关联业态，删除时却未上送业态，则不允许删除
        if (contract.getFormatId() != null && requestObj.getFormatId() == null) {
            throw new BusinessException("该合约已关联业态信息，请上送所属业态id！", "ECCT0009");
        }

        // 删除时上送的业态与合约关联业态的不一致，则不允许删除
        if (null != requestObj.getFormatId() && !requestObj.getFormatId().equals(contract.getFormatId())) {
            throw new BusinessException("该合约关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }

        // 非作废，仍保持原删除逻辑
        if (requestObj.getCanceled() == null || !requestObj.getCanceled()) {
            if (!SystemConsts.CONTRACT_STATUS_0.equals(contract.getStatus())) {
                throw new BusinessException("合约状态非未签署,不能删除", "ECCT0707");
            }

            if (!channel.equals(contract.getChannel())) {
                throw new BusinessException("上送渠道和当前渠道不一致", "ECCT0705");
            }

            if (!contract.getCreateBy().equals(operationUserId)) {
                throw new BusinessException("删除人和创建人不一致", "ECCT0706");
            }
            generalService.checkOnTheProcess(contract.getAppProcessNo(), requestObj);
        }

        // 合约更新前记录历史数据
        generalService.insertContractHistory(contract);

        // 逻辑删除合约
        contract.setIsDeleted(Integer.valueOf(SystemConsts.IS_DELETED_1));
        // 若为作废合约，更新合约作废状态
        if (requestObj.getCanceled() != null && requestObj.getCanceled()) {
            contract.setIsDeleted(Integer.valueOf(SystemConsts.IS_DELETED_2));
        }
        generalService.contractUpdateOperationIdAndDate(contract, requestObj.getOperationUserId(), true);
        contractMapper.updateById(contract);

        return SUCCESSFUL;
    }

    private void exportImage(String resourceName, InputStream in) {
        // 将生成的文件到桌面的目录下
        File file = new File("/Users/daigaole/Desktop/" + resourceName);
        // 将输入流的图片写到D盘下
        try {
            FileUtils.copyInputStreamToFile(in, file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 合约关系查询
     *
     * @param requestObj 关联合约查询请求
     * @return 合约详情信息传输对象
     */
    public List<ContractDto> queryRelationContract(QueryRelationContractReq requestObj) {

        List<Contract> contracts = this.contractMapper.queryRelationContract(requestObj.getContractId());

        List<ContractDto> contractDtos = new ArrayList<>();

        if (contracts != null) {
            contracts.forEach(contract -> {
                ContractDto contractDto = dozerBeanMapper.map(contract, ContractDto.class);

                contractDtos.add(contractDto);
            });
        }

        return contractDtos;
    }

    /**
     * 变更合约状态
     *
     * @param requestObj 变更合约状态请求类
     * @return 提示信息
     */
    @Transactional(rollbackFor = Exception.class)
    public String modifyContractStatus(ModifyContractStatusReq requestObj) {
        // 合约状态不能为空
        String status = requestObj.getStatus();
        Contract contract = generalService.verifyContractIdEffective(requestObj.getContractId());
        if (!contract.getChannel().equals(requestObj.getChannel())) {
            throw new BusinessException("上送渠道和当前渠道不一致", "ECCT0705");
        }

        // 合约已关联业态，更新状态时却未上送业态，则不允许更新
        if (contract.getFormatId() != null && requestObj.getFormatId() == null) {
            throw new BusinessException("该合约已关联业态信息，请上送所属业态id！", "ECCT0009");
        }

        // 更新状态时上送的业态与合约关联业态的不一致，则不允许更新
        if (null != requestObj.getFormatId() && !requestObj.getFormatId().equals(contract.getFormatId())) {
            throw new BusinessException("该合约关联业态信息与上送业态id不一致，请检查！", "ECCT0008");
        }

        // 如果更新状态为签署中必填审批流程编号
        if (SystemConsts.CONTRACT_STATUS_1.equals(status) && StringUtils.isEmpty(requestObj.getAppProcessNo())) {
            throw new BusinessException("审批流程编号不能为空", "ECCT0720");
        }

        if (SystemConsts.CONTRACT_STATUS_1.equals(status)) {// 开启审批流程
            contract.setAppProcessNo(requestObj.getAppProcessNo());
            contract.setStatus(status);
            generalService.contractUpdateOperationIdAndDate(contract, requestObj.getOperationUserId(), true);
        } else if (SystemConsts.CONTRACT_STATUS_2.equals(status)) {// 审批结束
            if (StringUtils.isEmpty(requestObj.getTsignatoryDate())) {
                throw new BusinessException("签署时间必填", "ECCT0721");
            }
            contract.setStatus(status);
            contract.setTsignatoryDate(generalService.dateToLocalDateTime(requestObj.getTsignatoryDate(), null));
            generalService.contractUpdateOperationIdAndDate(contract, requestObj.getOperationUserId(), true);
        } else if (SystemConsts.CONTRACT_STATUS_0.equals(status)) {// 审批驳回的时候，由渠道调用，修改状态为未签署。
            contract.setStatus(status);
            generalService.contractUpdateOperationIdAndDate(contract, requestObj.getOperationUserId(), true);
        } else {
            throw new BusinessException("输入正确的状态", "ECCT0720");
        }
        contractMapper.updateById(contract);
        return SUCCESSFUL;
    }

    /**
     * 根据模板 id 查询使用该模板的在途合约列表 <br />
     * 在途合约定义：为某笔业务中要签署的合约状态为“未签署” 或 “签署中”
     *
     * @param templateId 模板 id
     * @param channel 渠道标志
     * @return 在途合约列表
     * @author liulx
     * @date 2020年04月09日10:34:34
     */
    public List<ContractDto> listProcessingContractByTemplateId(Integer templateId, String channel) {
        List<Contract> list = this.list(getProcessingContractCondition(templateId, channel));
        return BeanUtil.mapList(dozerBeanMapper, list, ContractDto.class);
    }

    /**
     * 根据模板 id 查询使用该模板的在途合约数量
     *
     * @param templateId 模板 id
     * @param channel 渠道标志
     * @return 在途合约数量
     * @author liulx
     * @date 2020年04月09日10:36:11
     */
    public int countProcessingContractByTemplateId(Integer templateId, String channel) {
        return this.count(getProcessingContractCondition(templateId, channel));
    }

    /**
     * 获取在途合约查询条件
     *
     * @param templateId 模板 id
     * @param channel 渠道
     * @return 查询条件对象
     * @author liulx
     * @date 2020年04月09日10:32:02
     */
    public LambdaQueryWrapper<Contract> getProcessingContractCondition(Integer templateId, String channel) {
        LambdaQueryWrapper<Contract> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Contract::getTemplateId, templateId);
        queryWrapper.eq(Contract::getIsDeleted, SystemConsts.NOT_DELETED);
        queryWrapper.eq(Contract::getChannel, channel);
        queryWrapper.in(Contract::getStatus, SystemConsts.CONTRACT_STATUS_0, SystemConsts.CONTRACT_STATUS_1);
        return queryWrapper;
    }

    /**
     * 确认提交合约
     *
     * @param requestObj 确认提交合约请求类
     * @return 提交合约响应传输对象
     */
    @Transactional(rollbackFor = Exception.class)
    public ContractSubmissionDto confirmationContractSubmission(ContractSubmissionReq requestObj) {
        ContractSubmissionDto contractSubmissionDto = new ContractSubmissionDto();
        Contract contract = null;
        Integer contractId = requestObj.getContractId();
        String appProcessNo = null;
        if (contractId == null) {
            /**
             * 是新建合约确定提交
             */
            NewContract newContract = requestObj.getNewContract();
            if (newContract == null) {
                throw new BusinessException("新建参数不能为空", "ECCT0722");
            }
            NewContractReq newContractReq = dozerBeanMapper.map(newContract, NewContractReq.class);
            newContractReq.setChannel(requestObj.getChannel());
            newContractReq.setOperationUserId(requestObj.getOperationUserId());
            newContractReq.setTraceId(requestObj.getTraceId());
            newContractReq.setRequestTime(requestObj.getRequestTime());
            NewContractDto newContractDto = newContract(newContractReq);
            contractId = newContractDto.getId();
            contract = contractMapper.selectById(contractId);

        } else {
            /**
             * 是已经创建的了
             */
            ModifyContract modifyContract = requestObj.getModifyContract();
            if (modifyContract != null) {
                ModifyContractReq modifyContractReq = dozerBeanMapper.map(modifyContract, ModifyContractReq.class);

                modifyContractReq.setContractId(contractId);
                modifyContractReq.setChannel(requestObj.getChannel());
                modifyContractReq.setOperationUserId(requestObj.getOperationUserId());
                modifyContractReq.setTraceId(requestObj.getTraceId());
                modifyContractReq.setRequestTime(requestObj.getRequestTime());
                modifyContract(modifyContractReq);

            }
            contract = generalService.getContractById(contractId);
        }

        contractSubmissionDto.setId(contractId);
        contractSubmissionDto.setContractNo(contract.getContractNo());
        contractSubmissionDto.setOssUrl(contract.getOssUrl());
        contractSubmissionDto.setPdfOssUrl(contract.getPdfOssUrl());

        appProcessNo = contract.getAppProcessNo();
        if (appProcessNo == null) {
            appProcessNo = generalService.startProcessInstance(requestObj, contract.getContractNo());
            contract.setAppProcessNo(appProcessNo);
        } else {
            if (requestObj.getCompleteTask() == null) {
                requestObj.setCompleteTask(new CompleteTask());
            }
            generalService.completeTask(requestObj, appProcessNo);
        }

        contract.setStatus(SystemConsts.CONTRACT_STATUS_1);
        generalService.contractUpdateOperationIdAndDate(contract, requestObj.getOperationUserId(), true);
        contractMapper.updateById(contract);
        contractSubmissionDto.setAppProcessNo(appProcessNo);
        return contractSubmissionDto;
    }

    /**
     * 更新合约签署后文件地址
     *
     * @param req 更新合约签署后文件地址请求信息
     * @author liulx
     * @date 2020-10-19 15:59:36
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateContractSignedOssPath(UpdateContractSignedPathReq req) {
        Contract contract = validContract(req.getContractId(), req);
        contract.setPdfOssUrl(req.getPdfOssUrl());
        contract.setModifiedBy(req.getOperationUserId());
        contract.setGmtModified(LocalDateTime.now());
        contractMapper.updateById(contract);
    }

    /**
     * 校验合约是否有效正常
     *
     * @param contractId 合约 id
     * @param req 基础请求类
     * @return
     */
    public Contract validContract(Integer contractId, BaseReqObj req) {
        Contract contract = contractMapper.selectById(contractId);
        // 1、合约编号（合约编号）必须存在且有效
        if (contract == null) {
            throw new BusinessException("合约id无效", "ECCT0704");
        }

        if (!Integer.valueOf(SystemConsts.IS_DELETED_0).equals(contract.getIsDeleted())) {
            throw new BusinessException("合约不存在", "ECCT0711");
        }
        // 取消签章时合约渠道不一致的限制，可通过配置跨渠道访问 by liulx in 20211212 start
        // if (!req.getChannel().equals(contract.getChannel())) {
        // throw new BusinessException("上送渠道和当前渠道不一致", "ECCT0705");
        // }
        // 取消签章时合约渠道不一致的限制，可通过配置跨渠道访问 by liulx in 20211212 end
        return contract;
    }

    /**
     * 生成合约编号
     *
     * @param req 基础请求参数对象
     * @return 合约编号
     * @author liulx
     * @date 2020-12-02 10:25:29
     */
    @Transactional(rollbackFor = Exception.class)
    public ContractNoDTO generateContractNo(GenerateContractNoReq req) {
        String contractTypeCode;
        if (req.getTemplateId() != null) {
            Template template = generalService.getTemplate(req.getTemplateId(), req.getChannel());
            contractTypeCode = getContractTypeCode(template);
        } else if (org.apache.commons.lang3.StringUtils.isNotBlank(req.getContractTypeCode())) {
            contractTypeCode = req.getContractTypeCode();
        } else {
            throw new BusinessException("模板 id 和合约类型编码不能同时为空", "ECCT0001");
        }

        /*
            此处通过使用数据库主键自增来实现合约编号规则中最后的编号
         */
        Contract contract = new Contract();
        contract.setStatus(SystemConsts.CONTRACT_STATUS_0);
        contract.setChannel(req.getChannel());
        contract.setCreateBy(req.getOperationUserId());
        contract.setGmtCreate(LocalDateTime.now());
        contract.setModifiedBy(req.getOperationUserId());
        contract.setGmtModified(LocalDateTime.now());
        contract.setSignedWay("0");
        contract.setProductType("");

        // 保存来生成新的 id
        this.save(contract);
        Integer maxId = contract.getId();
        if (maxId == null) {
            throw new BusinessException("生成合约序号失败", "ECCT0740");
        }
        // 生成后再删除掉该记录
        this.removeById(contract);
        String contractNo =
            generalService.getContractNo(req.getChannel(), req.getCompanyCode(), contractTypeCode, maxId + 1, req);
        ContractNoDTO dto = new ContractNoDTO();
        dto.setContractNo(contractNo);
        ContractNo contractNoRecord = new ContractNo();

        contractNoRecord.setContractNo(contractNo);
        contractNoRecord.setGmtCreate(LocalDateTime.now());
        contractNoRecord.setCreateBy(req.getOperationUserId());
        contractNoRecord.setGmtModified(LocalDateTime.now());
        contractNoRecord.setModifiedBy(req.getOperationUserId());
        contractNoRecord.setChannel(req.getChannel());

        contractNoMapper.insert(contractNoRecord);
        return dto;
    }

    /**
     * 检查合约编号是否重复，重复则抛出异常
     *
     * @param contractNo 合约编号
     * @author liulx
     * @date 2020-12-02 10:25:29
     */
    public void checkContractNoRepeat(String contractNo) {
        LambdaQueryWrapper<Contract> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Contract::getContractNo, contractNo);
        queryWrapper.eq(Contract::getIsDeleted, SystemConsts.NOT_DELETED);
        int count = this.count(queryWrapper);
        if (count > 0) {
            throw new BusinessException("该合约编号已存在", "ECCT0741");
        }
    }

    /**
     * 校验合约编号是否符合规则 <br>
     * <b>该方法校验仅适合通过「生成合约编号」服务获取的合约编号</b> <br>
     * 「生成合约编号」服务
     * {@link ContractManagementServiceImpl#generateContarctNo(com.ynet.middleground.contract.bean.GenerateContractNoReq)}
     *
     * @param contractNo 合约编号
     * @author liulx
     * @date 2020-12-04 10:42:34
     */
    public void checkContractNoRule(String contractNo) {
        LambdaQueryWrapper<ContractNo> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ContractNo::getContractNo, contractNo);
        Integer count = contractNoMapper.selectCount(queryWrapper);
        if (count == null || count == 0) {
            throw new BusinessException("非法合约编号", "ECCT0742");
        }
    }

}
